/*
    请求管理模块：对请求进行管理，保证每个请求拿到属于自己的响应（接收响应的顺序并不一定是发送请求的顺序，所以响应具有时序问题，所以需要管理）
            同时能够发送所有消息类型的请求，此模块就是管理消息发送和按序接收响应的一个枢纽
    1、请求类
        1.1、响应回调：被注册给dispatcher模块，当接收到响应消息（所有响应消息类型）的时候，会调用该回调函数。
            函数内部应能够根据请求类型来决定响应的处理方式（设置给promise对象，让外部通过future对象来获取；或者调用回调）
        1.2、三种发送请求的接口：
            同步（直到获取到响应才返回）
            异步（不用等到响应就直接返回，给用户返回一个保存异步结果的对象，当有响应的时候从里边获取）
            回调（用户需要提供一个处理响应的回调函数，当接收到响应的时候在响应回调中调用）
*/
#pragma once
#include <future>
#include "../common/message.hpp"

namespace ns_jsonrpc
{
    namespace ns_client
    {
        class Requestor
        {
        public:
            using RequestorPtr = std::shared_ptr<Requestor>;
            using ResponseCallback = std::function<void(const BaseMessage::BaseMessagePtr &)>;
            using AsyncResponse = std::future<BaseMessage::BaseMessagePtr>;

        public:
            // 请求描述结构体
            struct RequestDescibe
            {
                using RequestDescibePtr = std::shared_ptr<RequestDescibe>;
                BaseMessage::BaseMessagePtr request;                // 请求消息
                RType rtype;                                        // 请求类型
                std::promise<BaseMessage::BaseMessagePtr> response; // 响应结果（设置/获取异步结果）
                ResponseCallback callback;                          // 响应回调（回调发送所需的回调函数）
            };

        public:
            // 响应回调
            void onResponse(const BaseConnection::BaseConnectionPtr &conn, BaseMessage::BaseMessagePtr &msg)
            {
                // 1.根据响应中的id查询对应的请求描述
                RequestDescibe::RequestDescibePtr rd = getRequestDescibe(msg->id());
                if (rd.get() == nullptr)
                {
                    LOG_ERROR("can not find request describe, request id: %s\n", msg->id());
                    return;
                }

                // 2.根据请求类型设置处理结果
                if (rd->rtype == RType::REQ_ASYNC)
                    rd->response.set_value(msg);
                else if (rd->rtype == RType::REQ_CALLBACK && rd->callback != nullptr)
                    rd->callback(msg);
                else
                    LOG_ERROR("unknown request type, request id: %s\n", msg->id());
                removeRequestDescibe(msg->id());
            }

            // 异步发送
            void send(const BaseConnection::BaseConnectionPtr &conn, const BaseMessage::BaseMessagePtr &req, AsyncResponse &asyncRsp)
            {
                // 1.创建请求描述（解决请求与响应时序问题）
                RequestDescibe::RequestDescibePtr rd = newRequestDescibe(req, RType::REQ_ASYNC);

                // 2.获取异步结果对象（外部通过此对象来获取结果）
                asyncRsp = rd->response.get_future();

                // 3.发送
                conn->send(req);
            }

            // 同步发送
            void send(const BaseConnection::BaseConnectionPtr &conn, const BaseMessage::BaseMessagePtr &req, BaseMessage::BaseMessagePtr &rsp)
            {
                // 同步发送的实现和异步一样，只不过需要我在函数内部代替用户阻塞线程，直到拿到结果返回
                AsyncResponse asyncRsp;
                send(conn, req, asyncRsp); // 调用异步发送
                rsp = asyncRsp.get();      // 线程阻塞等待，直到获取到结果
            }

            // 回调发送
            void send(const BaseConnection::BaseConnectionPtr &conn, const BaseMessage::BaseMessagePtr &req, const ResponseCallback &callback)
            {
                // 1.创建请求描述（解决请求与响应时序问题）
                RequestDescibe::RequestDescibePtr rd = newRequestDescibe(req, RType::REQ_CALLBACK, callback);

                // 2.发送（当接收到响应时，会自动调用回调函数）
                conn->send(req);
            }

        private:
            RequestDescibe::RequestDescibePtr newRequestDescibe(const BaseMessage::BaseMessagePtr &req, RType rtype, const ResponseCallback &callback = ResponseCallback())
            {
                std::unique_lock<std::mutex> lock(_mutex);
                RequestDescibe::RequestDescibePtr rd = std::make_shared<RequestDescibe>();
                rd->request = req;
                rd->rtype = rtype;
                rd->callback = callback;
                _requestsMap[req->id()] = rd;
                return rd;
            }

            RequestDescibe::RequestDescibePtr getRequestDescibe(const std::string &id)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                auto it = _requestsMap.find(id);
                if (it == _requestsMap.end())
                    return RequestDescibe::RequestDescibePtr();
                return it->second;
            }

            void removeRequestDescibe(const std::string &id)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                _requestsMap.erase(id);
            }

        private:
            std::map<std::string, RequestDescibe::RequestDescibePtr> _requestsMap; // <请求id，请求描述>
            std::mutex _mutex;
        };
    }
}
